package fr.umlv.ig.model;

import java.util.LinkedList;
import java.util.List;

import fr.umlv.ig.event.ItineraryEvent;
import fr.umlv.ig.event.LineEvent;
import fr.umlv.ig.event.StationEvent;
import fr.umlv.ig.event.TrainEvent;
import fr.umlv.ig.listener.ItineraryListener;
import fr.umlv.ig.listener.LineListener;
import fr.umlv.ig.listener.MetroListener;
import fr.umlv.ig.listener.StationListener;
import fr.umlv.ig.listener.TrainListener;
import fr.umlv.ig.listener.TrainsListener;


/**
 * In this class, methods to add listeners are implemented.
 * @author Jenseric Catbagan
 * @author William Choy
 * 
 */
public abstract class AbstractMetroModel implements MetroModel {
	/**Uses to forbidden remove {@link LineListener} while the model 
	   launch a fire for every {@link LineListener}.*/
	protected boolean firingLineListener = false;
	
	/**Uses to forbidden remove {@link StationListener} while the model 
	   launch a fire for every {@link StationListener}.*/
	protected boolean firingStationListener = false;
	
	/**Uses to forbidden remove {@link TrainListener} while the model 
	   launch a fire for every {@link TrainListener}.*/	
	protected boolean firingTrainListener = false;
	
	/**Uses to forbidden remove {@link ItineraryListener} while the model 
	   launch a fire for every {@link ItineraryListener}.*/		
	protected boolean firingItineraryListener = false;

	/**Uses to forbidden remove {@link TrainsListener} while the model 
	   launch a fire for every {@link TrainsListener}.*/		
	protected boolean firingTrainsListener= false;
	
	/**Uses to forbidden remove MetroListener while the model 
	   launch a fire for every MetroListener.*/		
	protected boolean firingMetroListener= false;

	/**List of {@link LineListener}*/
	private final List<LineListener> lineListeners           = new LinkedList<LineListener>();
	
	/**List of {@link StationListener}*/
	private final List<StationListener> stationListeners     = new LinkedList<StationListener>();
	
	/**List of {@link TrainListener}*/
	private final List<TrainListener> trainListeners         = new LinkedList<TrainListener>();

	/**List of {@link ItineraryListener}*/
	private final List<ItineraryListener> itineraryListeners = new LinkedList<ItineraryListener>();

	/**List of {@link TrainsListener}*/
	private final List<TrainsListener>  trainsListeners      = new LinkedList<TrainsListener>();


	private final List<MetroListener>  metroListeners    = new LinkedList<MetroListener>();


	////////////////////////////////////////////////////////////////////////////////
	//////////////////////////////methods for LineListener//////////////////////////
	////////////////////////////////////////////////////////////////////////////////	

	@Override
	public void addLineListener(LineListener listener) {
		if(firingLineListener) {
			throw new IllegalStateException("Cannot add listeners while fire... is using them");
		}
		lineListeners.add(listener);

	}


	@Override
	public void removeLineListener(LineListener listener) {
		if(firingLineListener) {
			throw new IllegalStateException("Cannot remove listeners while fire... is using them");
		}
		lineListeners.remove(listener);
	}


	/**
	 * Calls when the model want to prevent every 
	 * {@link LineListener} of a Line changed.
	 * 
	 * @param event the event to send to each {@link LineListener}
	 */
	protected void fireLineChanged(LineEvent event) {
		try {
			firingLineListener =true;
			for(LineListener l : lineListeners) {
				l.lineChange(event);
			}
		}
		finally {
			firingLineListener = false;
		}		
	}



	////////////////////////////////////////////////////////////////////////////////
	//////////////////////////////methods for StationListener///////////////////////
	////////////////////////////////////////////////////////////////////////////////


	@Override
	public void addStationListener(StationListener listener) {
		if(firingStationListener) {
			throw new IllegalStateException("Cannot remove listeners while fire... is using them");
		}
		stationListeners.add(listener);

	}


	@Override
	public void removeStationListener(StationListener listener) {
		if(firingStationListener) {
			throw new IllegalStateException("Cannot remove listeners while fire... is using them");
		}
		stationListeners.remove(listener);
	}

	/**
	 * Calls when the model want to prevent every 
	 * {@link StationListener} of a Station changed.
	 * 
	 * @param event the event to send to each {@link StationListener}.
	 */
	protected void fireStationChanged(StationEvent event) {
		try {
			firingStationListener =true;
			for(StationListener l : stationListeners) {
				l.stationChange(event);
			}
		}
		
		finally {
			firingStationListener = false;
		}
	}
	
	/**
	 * Calls when the model want to prevent every 
	 * {@link StationListener} of a Station changed with one of his neighbor.
	 * 
	 * @param event the event to send to each {@link StationListener}.
	 */
	protected void fireStationChanged(StationEvent event,Station nextStation) {
		try {
			firingStationListener =true;
			for(StationListener l : stationListeners) {
				l.nextStationChange(event, nextStation);
			}
		}
		finally {
			firingStationListener = false;
		}
	}
	
	/**
	 * Calls when the model want to prevent every 
	 * {@link StationListener} of a Station changed with one of his neighbor.
	 * 
	 * @param event the event to send to each {@link StationListener}.
	 */
	void fireStationChanged(StationEvent stationEvent,
			Station nextStation, Station direction) {
		try {
			firingStationListener =true;
			for(StationListener l : stationListeners) {
				l.nextStationChange(stationEvent, nextStation, direction);
			}
		}
		finally {
			firingStationListener = false;
		}
		
	}



	////////////////////////////////////////////////////////////////////////////////
	//////////////////////////////methods for TrainListener/////////////////////////
	////////////////////////////////////////////////////////////////////////////////

	@Override
	public void addTrainListener(TrainListener listener) {
		if(firingTrainListener) {
			throw new IllegalStateException("Cannot add listeners while fire... is using them");
		}
		trainListeners.add(listener);
	}


	@Override
	public void removeTrainListener(TrainListener listener) {
		if(firingTrainListener) {
			throw new IllegalStateException("Cannot remove listeners while fire... is using them");
		}
		trainListeners.remove(listener);
	}
	
	
	/**
	 * Calls when the model want to prevent every 
	 * {@link TrainListener} of a changed.
	 * 
	 * @param event the event to send to each {@link TrainListener}.
	 */
	protected void fireTrainChanged(TrainEvent event) {
		try {
			firingTrainListener =true;
			for(TrainListener l : trainListeners) {
				l.trainChange(event);
			}
		}
		finally {
			firingTrainListener = false;
		}		
	}


	////////////////////////////////////////////////////////////////////////////
	//////////////////////////////methods for Itinerary/////////////////////////
	////////////////////////////////////////////////////////////////////////////

	@Override
	public void addItineraryListener(ItineraryListener listener) {
		if(firingItineraryListener) {
			throw new IllegalStateException("Cannot remove listeners while fire... is using them");
		}		
		itineraryListeners.add(listener);
	}


	@Override
	public void removeItineraryListener(ItineraryListener listener) {
		if(firingItineraryListener) {
			throw new IllegalStateException("Cannot remove listeners while fire... is using them");
		}
		itineraryListeners.remove(listener);
	}
	

	/**
	 * Calls when the model want to prevent every 
	 * {@link ItineraryListener} of a changed.
	 * 
	 * @param event the event to send to each {@link TrainListener}.
	 */
	protected void fireItineraryChanged(ItineraryEvent event) {
		try {
			firingItineraryListener = true;
			for(ItineraryListener l : itineraryListeners) {
				l.itineraryChange(event);
			}
		}
		finally {
			firingItineraryListener = true;
		}

	}


	////////////////////////////////////////////////////////////////////////////////
	//////////////////////////////methods for TrainListener/////////////////////////
	////////////////////////////////////////////////////////////////////////////////

	@Override
	public void addTrainsListener(TrainsListener listener) {
		if(firingTrainsListener) {
			throw new IllegalStateException("Cannot remove listeners while fire... is using them");
		}		
		trainsListeners.add(listener);
	}


	@Override
	public void removeTrainsListener(TrainsListener listener) {
		if(firingTrainsListener) {
			throw new IllegalStateException("Cannot remove listeners while fire... is using them");
		}
		itineraryListeners.remove(listener);
	}


	/**
	 * Calls when the model want to prevent every 
	 * TrainsListener of a changed.
	 * 
	 */
	protected void fireAllTrain() {
		try {
			firingTrainListener = true;
			for(TrainsListener listener : trainsListeners) {
				listener.trainsMoved();
			}
		}
		finally {
			firingTrainsListener = false;
		}
	}

	
	

	
	protected void fireStartApplication() {
		try {
			firingMetroListener = true;
			for(MetroListener listener : metroListeners) {
				listener.initApplication();
			}
			
		}
		finally {
			firingMetroListener = false;
		}
		
	}
	
	
	@Override
	public void addMetroListener(MetroListener listener) {
		if(firingMetroListener) {
			throw new IllegalStateException("Cannot remove listeners while fire... is using them");
		}		
		metroListeners.add(listener);
	}


	@Override
	public void removeMetroListener(MetroListener listener) {
		if(firingMetroListener) {
			throw new IllegalStateException("Cannot remove listeners while fire... is using them");
		}
		metroListeners.remove(listener);
	}


}